package com.gdevelop.examples.demo.rebind;


import com.gdevelop.examples.demo.client.DemoConstants;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import java.net.URL;

import java.util.Stack;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;


public class GwtXPSource2Html extends Generator {
  private static final String GWTXP_EXT = "gwtxp.xml";
  private static final String SRC_DIR = "src";
  
  private TreeLogger logger;
  private GeneratorContext context;
  private Vector<File> gwtxpFiles;
  public GwtXPSource2Html() {
  }

  public String generate(TreeLogger logger, GeneratorContext context,
                         String typeName) throws UnableToCompleteException{
    this.logger = logger;
    this.context = context;
    gwtxpFiles = new Vector<File>();
    
    // Only generate files on the first permutation
    if (!isFirstPass()) {
      return null;
    }

    scanFiles(new File(SRC_DIR));
    
    for (File file : gwtxpFiles){
      InputStream in;
      try {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setValidating(true);
        factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
        ClassLoader classLoader = this.getClass().getClassLoader();
        URL url = classLoader.getResource("META-INF/gwtXP.xsd");
        factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", url.toString());
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(file);
        
        Element docElement = doc.getDocumentElement();
        String controllerClassName = docElement.getAttribute("controllerClassName");
        if (controllerClassName == null){
          logger.log(TreeLogger.ERROR, "No controller in " + file.getPath());
          throw new UnableToCompleteException();
        }

        String filename = file.getName();
        // gwtxp.xml source file
        in = new FileInputStream(file);
        String fileContents = getResourceContents(in);
        in.close();
        // Make the source pretty
        fileContents = fileContents.replace("<", "&lt;");
        fileContents = fileContents.replace(">", "&gt;");
        fileContents = fileContents.replace("* \n   */\n", "*/\n");
        fileContents = "<h2>" + filename + "</h2><pre>" + fileContents + "</pre>";

        // Controller source file
        in = this.getClass().getClassLoader().getResourceAsStream(controllerClassName.replace('.', '/') + ".java");
        String controllerContents = getResourceContents(in);
        in.close();
        // Make the source pretty
        controllerContents = controllerContents.replace("<", "&lt;");
        controllerContents = controllerContents.replace(">", "&gt;");
        controllerContents = controllerContents.replace("* \n   */\n", "*/\n");
        controllerContents = "<h2>" + controllerClassName + ".java</h2><pre>" + controllerContents + "</pre>";

        // Save the raw source in the public directory
        String dstPath = DemoConstants.DST_SOURCE_EXAMPLE + filename + ".html";
        
        createPublicResource(dstPath, fileContents + controllerContents);
      } catch (Exception e) {
        logger.log(TreeLogger.ERROR, "Error while gwtxp2html", e);
        throw new UnableToCompleteException();
      }
    }
    
    return null;
  }

  private void scanFiles(File base){
    Stack dirs = new Stack();
    dirs.push(base);
    while (!dirs.isEmpty()) {
      String s = dirs.pop().toString();
      File f = new File(s);
      if (f.exists() && f.isDirectory()) {
        String[] files = f.list();
        String ext;
        for (int i = 0; (files != null) && i < files.length; i++) {
          File f2 = new File(s, files[i]);
          if (f2.isDirectory()) {
            dirs.push(f2.getPath());
          } else {
            ext = files[i].substring(files[i].lastIndexOf('.') + 1);
            String file = files[i];
            if (file.endsWith(GWTXP_EXT)){
              gwtxpFiles.add(f2);
            }
          }
        }
      }
    }
  }

  /**
   * Get the full contents of a resource.
   * 
   * @param in the input stream of to the resource
   * @return the contents of the resource
   */
  private String getResourceContents(InputStream in)
      throws UnableToCompleteException {
    StringBuffer fileContentsBuf = new StringBuffer();
    BufferedReader br = null;
    try {
      br = new BufferedReader(new InputStreamReader(in));
      String temp;
      while ((temp = br.readLine()) != null) {
        fileContentsBuf.append(temp).append('\n');
      }
    } catch (IOException e) {
      logger.log(TreeLogger.ERROR, "Cannot read resource", e);
      throw new UnableToCompleteException();
    } finally {
      if (br != null) {
        try {
          br.close();
        } catch (IOException e) {
        }
      }
    }

    // Return the file contents as a string
    return fileContentsBuf.toString();
  }

  /**
   * Set the full contents of a resource in the public directory.
   * 
   * @param partialPath the path to the file relative to the public directory
   * @param contents the file contents
   */
  private void createPublicResource(String partialPath, String contents) {
    try {
      OutputStream outStream = context.tryCreateResource(logger, partialPath);
      outStream.write(contents.getBytes());
      context.commitResource(logger, outStream);
    } catch (UnableToCompleteException e) {
      logger.log(TreeLogger.ERROR, "Failed while writing", e);
    } catch (IOException e) {
      logger.log(TreeLogger.ERROR, "Failed while writing", e);
    }
  }

  /**
   * Ensure that we only generate files once by creating a placeholder file,
   * then looking for it on subsequent generates.
   * 
   * @return true if this is the first pass, false if not
   */
  private boolean isFirstPass() {
    String placeholder = DemoConstants.DST_SOURCE_EXAMPLE + "generated";
    try {
      OutputStream outStream = context.tryCreateResource(logger, placeholder);
      if (outStream == null) {
        return false;
      } else {
        context.commitResource(logger, outStream);
      }
    } catch (UnableToCompleteException e) {
      logger.log(TreeLogger.ERROR, "Unable to generate", e);
      return false;
    }
    return true;
  }
}
